//
// ElkinsEngine
//
// Copyright (C) 1992 Contemporary Design Studios. All rights reserved.
//


#import "ElkinsEngine.h"
#import <sys/time.h>
#import <stdio.h>


//
// Constants:
//
// See the journal article referenced in the header file.
//

#define M1	32771
#define M2	32779
#define M3	32783
#define F1	179
#define F2	183
#define F3	182

#define RANGE	32768

//#define RANGE	65536
#define MAXNUM	(RANGE - 1)


@implementation ElkinsEngine


//
// unit
//
// The ElkinsEngine, as defined in the article only uses 15 of the 16 bits
// in an unsigned short. We want full bytes of random bits, so we must
// throw away the partial byte.
//

+ (ulong)unit
{
    return 30;
}


//
// init
//

- init
{
    [super init];
    [self newSeeds];
    
    return self;
}


//
// initSeeds:::
//

- initSeeds:(ushort)s1
  :(ushort)s2
  :(ushort)s3
{
    [super init];
    [self setSeeds:s1 :s2 :s3];

    return self;
}


//
// newSeeds
//

- newSeeds
{
    struct timeval theTime;		// gettimeofday return structure
//    ushort foo;
    
    gettimeofday(&theTime, 0);		// Get the time of day in seconds and microseconds
    h1 = theTime.tv_usec % RANGE;	// Set seed 1 by microseconds past second
    gettimeofday(&theTime, 0);		// Get the time of day in seconds and microseconds
    h2 = theTime.tv_usec % RANGE;	// Set seed 2 by microseconds past second
    gettimeofday(&theTime, 0);		// Get the time of day in seconds and microseconds
    h3 = theTime.tv_usec % RANGE;	// Set seed 3 by microseconds past second

//    printf("ElkinsEngine: Seeds set to: %d %d %d\n", (long)h1, (long)h2, (long)h3);

//    [self makeRandom:(char *)(&foo)];

    return self;	
}


//
// setSeeds:::
//

- setSeeds:(ushort) s1 :(ushort) s2 :(ushort) s3
{
//    ushort	foo;
    
    h1 = s1;				// Set the seeds to the values given
    h2 = s2;
    h3 = s3;
    
//    [self makeRandom:(char *)(&foo)];
    
    return self;
}


//
// getSeeds:::
//

- getSeeds:(ushort *)s1 :(ushort *)s2 :(ushort *)s3
{
    if((s1 == NULL) || (s2 == NULL) || (s3 == NULL))
	return nil;

    *s1 = h1;
    *s2 = h2;
    *s3 = h3;

    return self;
}


//
// makeRandom:
//
// See the Source article for the explanations of these constants:
//

- makeRandom:(uchar *)storage
{
    ushort	temp;
    int		i;
    ushort	*out = (ushort *)storage;
    
    do{
	h1 = (F1 * h1) % M1;					// Update the sections
	h2 = (F2 * h2) % M2;
	h3 = (F2 * h3) % M3;
    } while ((h1 > MAXNUM) || (h2 > MAXNUM) || (h3 > MAXNUM));	// If a section is out of range,
    
    temp = ((h1 + h2 + h3) % RANGE);
    
    for(i = 0; i < 15; i++) {
	do{
	    h1 = (F1 * h1) % M1;
	    h2 = (F2 * h2) % M2;
	    h3 = (F2 * h3) % M3;
	} while ((h1 > MAXNUM) || (h2 > MAXNUM) || (h3 > MAXNUM));
        
	//
	// Save the value, and grab a bit from temp;
	//
	
	out[i] = (ushort)((h1 + h2 + h3) % RANGE) + ((temp & 0x0001) ? 0x8000 : 0x0000);
        temp = temp >> 1;
    }
    
    
    
    return self;
}


//
// read:
//

- read:(NXTypedStream *)stream
{
    int		t1;			// Stuff h's into ints for temporary.
    int		t2;
    int		t3;
    
    [super read:stream];
    
    NXReadTypes(stream, "iii", &t1, &t2, &t3);
    
    h1 = (ushort)t1;
    h2 = (ushort)t2;
    h3 = (ushort)t3;
    
    return self;
}


//
// write:
//

- write:(NXTypedStream *)stream
{
    int		t1 = (int)h1;		// Stuff h's into ints for temporary.
    int		t2 = (int)h2;
    int		t3 = (int)h3;
    
    [super write:stream];
    
    NXWriteTypes(stream, "iii", &t1, &t2, &t3);

    return self;
}


@end


//
// End of file.
//